<template>
  <!-- 重编辑可视化弹窗 -->
  <!-- modified from /src/components/studio/data/drawer/VisualWindow.vue -->
  <div>
    <a-modal
      :destroy-on-close="true"
      :visible="visible"
      :width="modalWidth"
      :dialog-style="dialogStyle"
      :mask-closable="false"
      :mask="false"
      class="visualization-preview-modal"
      @ok="handleOk"
      @cancel="handleCancel"
    >
      <template slot="title">
        <div class="modal-title" @mousedown="onDragStart($event)">
          {{ title }}
        </div>
      </template>
      <template slot="footer">
        <!-- <a-button
          v-show="enableCleanAction"
          :disabled="!isCleaning"
          type="primary"
          @click="handleDataClean"
          >清洗</a-button
        >
        <a-button
          v-show="isShowDimensionalityReduction"
          @click="handleDimensionalityReductionClick"
          >降维</a-button
        > -->
        <a-button type="default" @click="handleCancel">取消</a-button>
        <a-button v-if="showSaveBtn" type="primary" @click="handleOk"
          >保存可视化
        </a-button>
        <span
          class="resize-nwse"
          @mousedown="($event) => onResizeStart($event, 'nwse')"
        />
      </template>
      <div
        class="modal-content"
        :style="{
          height: `${
            modalHeight -
            modalExtraStyle.headerHeight -
            modalExtraStyle.footerHeight
          }px`,
        }"
      >
        <a-row>
          <a-col :span="24">
            <div
              class="window-middle"
              :style="{
                height: `${
                  modalHeight -
                  modalExtraStyle.headerHeight -
                  modalExtraStyle.footerHeight
                }px`,
              }"
            >
              <!-- <div class="clean-action">
                <div v-show="isShowBrushCleanAction" class="brush-clean-action">
                  <div class="wrapper">
                    <span
                      v-for="data in brushDataInfo.slice(0, 3)"
                      :key="data.field"
                      class="brush-data-info"
                    >
                      <label>{{ data.field }}:</label>{{ data.text }}
                    </span>
                    <a-popover v-if="brushDataInfo.length > 3">
                      <template slot="content">
                        <div v-for="data in brushDataInfo" :key="data.field">
                          {{ data.field }}: {{ data.text }}
                        </div>
                      </template>
                      <span class="btn-more">更多</span>
                    </a-popover>
                  </div>
                  <a-select
                    v-model="keepCleanSubType"
                    allow-clear
                    :placeholder="brushCleanOption[0].label"
                    size="small"
                    style="margin-right: 10px; width: 100px;"
                    :dropdown-match-select-width="false"
                    @change="onBrushActionChange"
                  >
                    <a-select-option
                      v-for="option in brushCleanOption[0].children"
                      :key="option.value"
                      :value="option.value"
                      >仅保留{{ option.label }}</a-select-option
                    >
                  </a-select>

                  <a-select
                    v-model="delCleanSubType"
                    allow-clear
                    :placeholder="brushCleanOption[1].label"
                    size="small"
                    style="width: 100px;"
                    :dropdown-match-select-width="false"
                    @change="onBrushActionChange"
                  >
                    <a-select-option
                      v-for="option in brushCleanOption[1].children"
                      :key="option.value"
                      :value="option.value"
                      >删除{{ option.label }}</a-select-option
                    >
                  </a-select>

                </div>
                <div
                  v-show="isShowLegendCleanAction"
                  class="legend-clean-action"
                >
                  <span class="brush-data-info">
                    <label>当前选中分类:</label
                    >{{ legendSelectedItems.slice(0, 3).join('、') }}
                    <a-popover v-if="legendSelectedItems.length > 3">
                      <template slot="content">
                        <div v-for="k in legendSelectedItems" :key="k">
                          {{ k }}
                        </div>
                      </template>
                      等{{ legendSelectedItems.length }}个
                    </a-popover>
                    <label style="margin-left: 16px;">清洗操作: 保留分类</label>
                  </span>
                </div>
              </div> -->
              <div
                class="window-middle-canvas"
                :style="{ height: `${chartSize[1]}px` }"
              >
                <Chart
                  v-if="chartVisible"
                  ref="chart"
                  :chart-type="currentGridItem.chartType"
                  :config="currentGridItem.chartOptions"
                  @chartinit="onChartInit"
                ></Chart>
                <div v-else class="empty-info-box">
                  <img :src="currentChartEmptyImg" />
                  <div class="empty-info">请先配置数据</div>
                </div>
              </div>
            </div>
          </a-col>
        </a-row>
      </div>
    </a-modal>
  </div>
</template>

<script lang="ts">
import Vue from 'vue'
import Component from 'vue-class-component'
import Chart from '@/components/chart/chart.vue'
import DataStore from '@/store/modules/dataview'
import { cloneDeep, flatten } from 'lodash'
import { Prop, Watch } from 'vue-property-decorator'
import Big from 'big.js'
import { CleanFromVisAction } from '@/util/data-selection-actions'
import { ChartDetailsEnum, chartMaps } from '@/config/contant'
import { simpleUpdate, taskComplexLoad } from '@/api/task'
import { publicPath } from '@/api/constants'
import { detailDefaultOptions } from '@/components/common/VisualizationComp/default-chart-options'

@Component({
  components: { Chart },
})
export default class VisualizationPreviewModal extends Vue {
  @Prop({ default: false }) visible!: boolean
  @Prop({ default: '' }) title!: string
  @Prop() chart!: any // 当前需要渲染的chart的一些配置信息
  @Prop({ default: true }) showSaveBtn!: boolean

  private publicPath = publicPath // 基础URL
  private modalWidth: number = 900
  private modalHeight: number = 588
  private modalExtraStyle = {
    // 用来和modalHeight结合计算chart的size
    headerHeight: 54, // modal title高度
    footerHeight: 54, // modal footer 高度
    canvasMargin: [36, 30, 30, 30], // modal content的上下左右margin
  }
  private dialogStyle: any = {
    top: '20px',
    left: '50px',
  }
  private chartInstance: any = null // 图表实例
  private chartOriginValue: any[] = [] // 初始图表配置中的value

  private brushData: { currentRange?: any[] } = {} // 图表框选出的数据

  private isCleaning: boolean = false // 是否在清洗筛选中

  private cleanActionType: string = '' // 清洗的大类型：保留|删除
  private keepCleanSubType = [] // keep清洗大类下的小类
  private delCleanSubType = [] // delete清洗大类下的小类

  // private brushCleanAction = [] // 级联组件中清洗的value
  private legendCleanAction = [] //

  private legendItems: string[] = []
  private legendSelectedItems: string[] = [] // 点击选中的legend内容
  private isDraging: boolean = false // 是否拖动中
  private isResizing: boolean = false // 是否resize中
  private resizingMode: string = '' // resizing的类型，为后续4个角resize扩展
  private originMouseData: { x?: number; y?: number } = {} // 拖动初始鼠标参数
  private resizeMouseData: { x?: number; y?: number } = {}
  private initialChartSize: number[] = [840, 420] // chart 初始大小
  private documentHeight: number = document.documentElement.clientHeight

  private brushCleanOption: any[] = [
    {
      value: 'keep',
      label: '仅保留',
      children: [
        {
          value: 'keep-brush',
          label: '框选区域数据',
        },
        {
          value: 'keep-brushX',
          label: 'X 轴区域数据',
        },
        {
          value: 'keep-brushY',
          label: 'Y 轴区域数据',
        },
      ],
    },
    {
      value: 'delete',
      label: '删除',
      children: [
        {
          value: 'delete-brush',
          label: '框选区域数据',
        },
        {
          value: 'delete-brushX',
          label: 'X 轴区域数据',
        },
        {
          value: 'delete-brushY',
          label: 'Y 轴区域数据',
        },
      ],
    },
  ]

  private allGraphs: { [key: string]: any[] } = {
    'bar-chart': [
      {
        name: 'barChart',
        title: '柱状图',
        chartType: 'barChart',
        imgSrc: '/icon/components/barChart.png',
      },
      {
        name: 'groupBarChart',
        title: '分组柱状图',
        chartType: 'barChart',
        imgSrc: '/icon/components/groupBarChart.png',
      },
      {
        name: 'stackBarChart',
        title: '堆栈柱状图',
        chartType: 'barChart',
        imgSrc: '/icon/components/stackBarChart.png',
      },
    ],
    'pie-chart': [
      {
        name: 'pieChart',
        title: '饼图',
        chartType: 'pieChart',
        imgSrc: '/icon/components/pieChart.png',
      },
      {
        name: 'donutChart',
        title: '圆环图',
        chartType: 'pieChart',
        imgSrc: '/icon/components/donutChart.png',
      },
    ],
    'line-chart': [
      {
        name: 'lineChart',
        title: '折线图',
        chartType: 'lineChart',
        imgSrc: '/icon/components/lineChart.png',
      },
    ],
    'dot-chart': [
      {
        name: 'scatterplot',
        title: '散点图',
        chartType: 'scatterplot',
        imgSrc: '/icon/components/scatterplot.png',
      },
    ],
    'area-chart': [
      {
        name: 'areaChart',
        title: '面积图',
        chartType: 'areaChart',
        imgSrc: '/icon/components/areaChart.png',
      },
    ],
    stock: [
      {
        name: 'parallelCoordinates',
        title: '平行坐标轴',
        chartType: 'parallelCoordinates',
        imgSrc: '/icon/components/parallelCoordinates.png',
      },
    ],
    'heat-map': [
      {
        name: 'heatmapMatrix',
        title: '热力图',
        chartType: 'heatmapMatrix',
        imgSrc: '/icon/components/heatmapMatrix.png',
      },
    ],
    'map-chart': [
      {
        name: 'geographicMap',
        title: '地图',
        chartType: 'geographicMap',
        imgSrc: '/icon/components/map.png',
      },
    ],
  }

  @Watch('visible')
  private onVisibleChange() {
    if (this.visible) {
      this.modalHeight = 588
      this.modalWidth = 900
      this.initialChartSize = [840, 420]
      this.dialogStyle = {
        top: '20px',
        left: `${
          (document.documentElement.clientWidth - this.modalWidth) / 2
        }px`,
      }
    }
  }

  private get brushDataInfo() {
    const { currentRange } = this.brushData
    if (!currentRange || currentRange.length === 0) {
      return []
    }
    return currentRange.map((d: any) => {
      return {
        field: d.field,
        text:
          d.type === 'linear'
            ? `${new Big(d.min).toFixed(2)}-${new Big(d.max).toFixed(2)}`
            : `${d.values.slice(0, 2).join('、')}等${d.values.length}个值`,
      }
    })
  }

  private get enableCleanAction() {
    // 是否显示清洗按钮
    if (!this.chart) {
      return false
    }
    return this.enableBrushEvents || this.enableLegendEvents
  }

  private get enableBrushEvents() {
    // 是否开启brush事件
    if (!this.chart) {
      return false
    }
    const [, chartType] = this.chart.chartType
    const { cleanActions } = this.chart.chartOptions
    const disableType = chartMaps.pieChart.map((c) => c.chartType)
    return !disableType.includes(chartType) && cleanActions.includes('brush')
  }

  private get enableLegendEvents() {
    // 是否开启legend点击事情，colorField：用于着色的字段
    const [, chartType] = this.chart.chartType
    const disableType = chartMaps.pieChart.map((c) => c.chartType)
    return disableType.includes(chartType) || this.chart.chartOptions.colorField
  }

  private get chartSize() {
    const { headerHeight, footerHeight, canvasMargin } = this.modalExtraStyle
    const verticalMargin = canvasMargin[0] + canvasMargin[2]
    const horizontalMargin = canvasMargin[1] + canvasMargin[3]
    return [
      this.modalWidth - horizontalMargin,
      this.modalHeight - headerHeight - footerHeight - verticalMargin,
    ]
  }

  /**
   * 是否显示清洗操作区域
   */
  private get isShowBrushCleanAction() {
    const cleanActions = this.chart?.chartOptions?.cleanActions || []
    const currentRange = this.brushData.currentRange || []
    return (
      (this.isCleaning && this.legendSelectedItems.length === 0) ||
      (currentRange.length > 0 && cleanActions.length > 0)
    )
  }

  private get isShowLegendCleanAction() {
    const cleanActions = this.chart?.chartOptions?.cleanActions || [] // 允许的clean操作
    const currentRange = this.brushData.currentRange || []
    const chartType = this.chart?.chartType.slice(-1)[0]
    return (
      this.legendSelectedItems.length > 0 &&
      currentRange.length === 0 &&
      cleanActions.length > 0 &&
      chartType !== ChartDetailsEnum.parallelCoordinates
    )
  }

  private get selectNode() {
    return DataStore.selectNode
  }

  private get currentHistory() {
    return DataStore.currentHistory
  }

  private get tableName(): string {
    // 查询数据的表名取决于当前历史记录的表名，没有的话取selectnode的表名
    // if (DataStore.autojoinTableName) { // autojoin 预览表数据
    //   return DataStore.autojoinTableName
    // }
    return (
      this.currentHistory?.data?.table ??
      this.selectNode?.data?.output?.[0]?.tableName ??
      ''
    )
  }

  /**
   * 是否显示降维按钮，特定算子节点 + 平行坐标轴展示
   */
  private get isShowDimensionalityReduction() {
    if (!this.selectNode || !this.chart) {
      return false
    }
    const { algType, subType } = this.selectNode.data
    const chartType = this.chart.chartType.slice(-1)[0]
    const mapping: any = {
      // subType-algType
      '1-1': 'DBSCAN',
      '1-2': 'KMEANS',
      '4-8': 'ISOFOREST',
      '3-4': 'PCA',
      '3-5': 'TSNE',
      '3-6': 'LLE',
    }

    return (
      !!mapping[`${subType}-${algType}`] &&
      chartType === ChartDetailsEnum.parallelCoordinates
    )
  }

  private get currentGridItem() {
    if (!this.chart) {
      return null
    }

    const result: any = {
      chartType: this.chart.chartType,
      chartOptions: {
        ...this.chart.chartOptions,
        size: this.initialChartSize,
        axisTitleIsShow: true,
        xAxisTitleFontSize: 16,
        yAxisTitleFontSize: 16,
        // labelIsShow: true,
        padding: [20, 20, 20, 20],
        // pointRadius: 3,
        ...detailDefaultOptions,
        xAxisTickIsShow: true,
        yAxisTickIsShow: true,
      },
    }
    // if(this.chart && this.chart.chartType.includes('parallelCoordinates')){
    // result.chartOptions.legendIsShow = false
    // }
    //  if (this.chart && this.chart.chartType === 'scatterplot' || this.chart.chartType[0] === 'scatterplot') {
    result.chartOptions.legendIsShow = true
    //  }
    if ([1, -1, '1', '-1'].includes(result.chartOptions.value?.[0]?.label)) {
      const abnormalIndex = result.chartOptions.value.findIndex((v: any) =>
        [-1, '-1'].includes(v.label)
      )
      if (abnormalIndex > 0) {
        const [v] = result.chartOptions.value.splice(abnormalIndex, 1)
        result.chartOptions.value = [v, ...result.chartOptions.value]
      }
    }

    return result
  }

  // 获取当前图表默认背景图片
  public get currentChartEmptyImg() {
    if (this.currentGridItem) {
      const currentChart = flatten(Object.values(this.allGraphs)).find(
        (item) => item.name === this.currentGridItem!.chartType
      )
      if (currentChart) {
        return this.publicPath + currentChart.imgSrc
      }
    }
    return ''
  }

  get chartVisible() {
    if (this.currentGridItem) {
      return true
    }
    return false
  }

  private get projectId() {
    return Number(this.$route.params.id)
  }

  private get pipelineId() {
    return DataStore.getPipelineId
  }

  // 点击降维按钮
  public handleDimensionalityReductionClick() {
    const { algType, subType } = this.selectNode.data
    const PCAMapping: any = {
      // 降维系列节点
      '3-4': 'PCA',
      '3-5': 'TSNE',
      '3-6': 'LLE',
    }
    const string = PCAMapping[`${subType}-${algType}`] ? '' : '再执行算子逻辑'
    this.$confirm({
      title: '确定要对输入数据进行降维吗？',
      content: `点击确定后会对原数据降至二维${string}`,
      okText: '确定',
      cancelText: '取消',
      onOk: () => {
        if (PCAMapping[`${subType}-${algType}`]) {
          // 降维系列节点直接改参数降至二维
          this.updatePCAParameter()
        } else {
          // 在节点前增加PCA降维节点
          this.addPCANode()
        }
      },
    })
  }

  public updatePCAParameter() {
    // 降维系列节点直接改参数降至二维
    const { setParams, algType } = this.selectNode.data
    setParams.forEach((p: any) => {
      if (p.english_name === 'dimension_num') {
        // 改降维参数
        p.value = 2
      }
    })
    const parameters = {
      id: this.selectNode.id,
      name: this.selectNode.name,
      projectId: this.selectNode.projectId,
      pipelineId: this.selectNode.pipelineId,
      parentId: this.selectNode.parentId,
      childId: this.selectNode.childId,
      type: this.selectNode.type,
      data: {
        algType,
        setParams,
      },
    }

    this.updateOperatorParameters(parameters)
  }

  /**
   * 更新算子节点参数
   */
  public updateOperatorParameters(parameters: any) {
    simpleUpdate({ data: parameters })?.then((response: any) => {
      if (response.data.code === 100) {
        this.handleCancel()
        this.$message.success('降维成功', 1)
        DataStore.setConfirmEditTime(new Date().getTime())
        // 更新pipeline
        DataStore.queryPipelineNodes({
          id: this.selectNode.projectId,
          pipelineId: this.selectNode.pipelineId,
        })
      }
    })
  }

  public addPCANode() {
    // 添加PCA降维节点
    const { parentId, id } = this.selectNode
    const parameters = {
      name: 'PCA_DENSE',
      projectId: this.projectId,
      pipelineId: this.pipelineId,
      parentId,
      childId: id,
      type: 1, // 类型: 3--表示数据节点， 1--算子， 2--ETL算子
      data: {
        algName: 'PCA_DENSE',
        algType: 4,
        position: {
          row: 0,
          col: 0,
        },
      },
    }

    taskComplexLoad({ data: parameters })?.then((response: any) => {
      if (response.data.code === 100) {
        this.handleCancel()
        this.updatePipeline()
        this.$message.success('添加降维节点成功 !', 1)
      }
    })
  }

  public async updatePipeline() {
    if (this.pipelineId != null) {
      const parameters = {
        id: this.projectId,
        pipelineId: this.pipelineId,
      }
      await DataStore.queryPipelineNodes(parameters)
      DataStore.setConfirmEditTime(new Date().getTime())
    }
  }

  public handleCancel() {
    this.$emit('close')
    DataStore.setColumnFilters([])
    this.resetCleanData()
  }

  public handleOk() {
    if (this.isCleaning) {
      this.$confirm({
        title: '当前筛选数据中，保存可视化前是否进行清洗？',
        okText: '确定',
        cancelText: '取消',
        onOk: () => {
          this.handleDataClean()
          this.$emit('save-chart')
        },
        onCancel: () => {
          this.$emit('save-chart')
          this.$emit('close')
          this.resetCleanData()
        },
      })
    } else {
      this.$emit('save-chart')
      this.$emit('close')
      this.resetCleanData()
    }
  }

  private resetCleanData() {
    this.brushData = {}
    this.legendSelectedItems = []
    this.legendCleanAction = []
    this.isCleaning = false
    this.keepCleanSubType = []
    this.delCleanSubType = []
  }

  private onChartInit(instance: any) {
    this.chartInstance = instance
    this.chartOriginValue = cloneDeep(this.chart.chartOptions.value)
    // this.bindChartEvents()
  }

  private bindChartEvents() {
    // this.bindChartElementEvents()
    this.bindChartBrushEvents()
    this.bindChartLegendEvents()
  }

  /**
   * 初始化图例分类项
   */
  private initLegendCategory() {
    const { labelKey, value, colorField } = this.currentGridItem.chartOptions
    const data: Set<string> = new Set()
    value.forEach((v: any) => {
      if (colorField) {
        // 存在指定的分类字段
        data.add(v[colorField])
      } else {
        data.add(v[labelKey])
      }
    })
    if (data.size > 1) {
      this.legendItems = [...data]
      this.legendSelectedItems = [...data]
    }
  }

  /**
   * 绑定图例事件
   */
  private bindChartLegendEvents() {
    if (!this.enableLegendEvents) {
      return
    }
    this.initLegendCategory()
    this.chartInstance.$on('legend-item:click', (ev: any) => {
      const { target } = ev
      const delegateObject = target.get('delegateObject')
      const { name } = delegateObject.item // 图例选项
      const { legendSelectedItems } = this
      // this.legendCleanAction = []
      this.isCleaning = true
      if (legendSelectedItems.includes(name)) {
        this.legendSelectedItems = this.legendSelectedItems.filter(
          (d) => d !== name
        )
      } else {
        this.legendSelectedItems.push(name)
      }
      if (this.legendSelectedItems.length === 0) {
        this.isCleaning = false
      }
      this.onLegendActionChange('keep')
    })
  }

  // private bindChartElementEvents() {
  //   this.chartInstance.$on('element:contextmenu', (ev: any) => {
  //     // debugger
  //     // const target:any = {ev}
  //     // const delegateObject = target.get('delegateObject')
  //     // const item = {delegateObject}
  //     ev.event.preventDefault()
  //     console.log(ev)
  //   })

  // }

  /**
   * 初始化图表的框选事件
   */
  private bindChartBrushEvents() {
    if (!this.enableBrushEvents) {
      return
    }
    const [, chartType] = this.chart.chartType
    const type =
      chartType === ChartDetailsEnum.parallelCoordinates ? 'p-brush' : 'brush'

    this.chartInstance.$on(type, (data: any) => {
      this.keepCleanSubType = []
      this.delCleanSubType = []
      this.isCleaning = false

      if (chartType === ChartDetailsEnum.parallelCoordinates) {
        if (!data.currentRange || data.currentRange.length === 0) {
          this.brushData = { currentRange: [] }
        } else {
          this.brushData = data
        }
      } else {
        this.brushData = data
        if (!data.currentRange) {
          // this.brushCleanAction = []
          this.legendSelectedItems = this.legendItems
          this.updateChartData('reset', [])
        } else {
          this.legendSelectedItems = []
        }
      }
    })
  }

  /**
   * 选择 仅保留/删除 操作
   */
  private onBrushActionChange(value: string) {
    if (!value) {
      // 清空框选的清洗action操作
      this.isCleaning = false
      this.cleanActionType = ''
      this.updateChartData('reset', [])
      DataStore.setColumnFilters([]) // 清空筛选项
    } else {
      this.isCleaning = true
      const actionType = value.split('-')[0] // 清洗类型
      this.cleanActionType = actionType
      if (actionType === 'keep') {
        // 选择一个，清空另一个的选项
        this.delCleanSubType = []
      } else {
        this.keepCleanSubType = []
      }
      const subAction = value.replace(/^(keep|delete)-/, '')
      this.setCleanFilters(actionType as 'keep' | 'delete', subAction)
    }
  }

  /**
   * legend事件清洗
   */
  private onLegendActionChange(value: string) {
    if (value) {
      const { chartOptions, chartType } = this.chart
      this.brushData = {}
      this.cleanActionType = value
      const col = chartOptions.colorField || chartOptions.labelKey
      const filters = [
        [
          {
            col,
            filterType: value === 'keep' ? '=' : '<>',
            values: this.legendSelectedItems,
          },
        ],
      ]
      const pieChartType = chartMaps.pieChart.map((c) => c.chartType) // 饼图、圆环图等
      if (pieChartType.includes(chartType.slice(-1)[0])) {
        const { type, field } = this.parsePieChartLegendAction()
        filters[0][0].values = field
        if (type === 'reverse') {
          // 反向处理
          filters[0][0].filterType = value === 'keep' ? '<>' : '='
        }
      }
      DataStore.setColumnFilters(filters)
    } else {
      this.isCleaning = false
      this.cleanActionType = ''
      DataStore.setColumnFilters([]) // 清空筛选项
    }
  }

  /**
   * 针对饼图中超过阈值的字段（其他）进行特殊处理
   */
  private parsePieChartLegendAction() {
    const field = this.chart.chartOptions.value.filter(
      (v: any) => v.groupValue && v.groupValue.length > 0
    )[0] // 存在“其他”字段：字段中的 groupValue 是合并的所有字段
    if (!field || !this.legendSelectedItems.includes('其他')) {
      return {
        type: 'normal',
        field: this.legendSelectedItems,
      }
    }
    // 包含“其他”字段，为了保证图例全部项目的选择正确，需反向处理(仅保留a、d、e = 排除b、c)
    return {
      type: 'reverse',
      field: this.legendItems.filter(
        (v) => !this.legendSelectedItems.includes(v)
      ),
    }
    // // 存在合并的字段
    // return [
    //   ...this.legendSelectedItems.filter(v => v !== '其他'),
    //   ...(this.legendSelectedItems.includes('其他') ? field.groupValue.map((item: any) => item[col]) : [])
    // ]
  }

  /**
   * 交互后更新图表数据
   */
  private updateChartData(
    cleanAction: 'keep' | 'delete' | 'reset',
    filters: { col: string; type: 'linear' | 'cat'; values: string[] }[]
  ) {
    const { chartOriginValue } = this
    let newValue = chartOriginValue
    if (filters.length > 0) {
      newValue = chartOriginValue.filter((value) => {
        const flag = filters.every((filter) => {
          if (filter.type === 'linear') {
            return (
              value[filter.col] >= +filter.values[0] &&
              value[filter.col] < +filter.values[1]
            )
          }
          return filter.values.includes(value[filter.col])
        })
        return cleanAction === 'keep' ? flag : !flag
      })
    }

    const data = { ...this.currentGridItem.chartOptions, size: this.chartSize }
    data.value = cleanAction === 'reset' ? chartOriginValue : newValue
    this.chartInstance.update(data)
  }

  /**
   * 设置brush时筛选的数据
   */
  private setCleanFilters(cleanAction: 'keep' | 'delete', subAction: string) {
    const filters = []
    const currentRange = this.brushData.currentRange || []

    const brushValue = currentRange.map((item) => {
      return {
        col: item.field,
        filterType: this.parseFilterType(cleanAction, item.type),
        type: item.type,
        values:
          item.type === 'linear'
            ? [new Big(item.min).toFixed(2), new Big(item.max).toFixed(2)]
            : item.values,
      }
    })

    const chartFilters = []
    switch (subAction) {
      case 'brush':
        brushValue.forEach((item) => filters.push([item]))
        chartFilters.push(...brushValue)
        break
      case 'brushX':
        filters.push([brushValue[0]])
        chartFilters.push(brushValue[0])
        break
      case 'brushY':
        filters.push([brushValue[1]])
        chartFilters.push(brushValue[1])
        break
      default:
        break
    }
    this.updateChartData(cleanAction, chartFilters)
    DataStore.setColumnFilters(filters)
  }

  /**
   * 解析filterType
   */
  private parseFilterType(
    cleanAction: 'keep' | 'delete',
    brushAxisType: 'linear' | 'cat'
  ) {
    // linear：连续，cat：分类
    if (cleanAction === 'keep') {
      return brushAxisType === 'linear' ? '[a,b)' : '='
    }
    return brushAxisType === 'linear' ? '![a,b)' : '<>'
  }

  /**
   * 触发清洗操作
   */
  private async handleDataClean() {
    const filter = DataStore.columnFilters
    const data = {
      filter,
      action: 'FILTER',
      description: this.formatDescription(filter),
      actionType: CleanFromVisAction.cleanFromVis,
      id: this.currentHistory ? this.currentHistory.data.id : '',
      table: this.tableName,
    }

    if (+this.selectNode.type !== 5) {
      // 当前节点不是清洗算子节点，需要新加一个清洗算子节点然后进行筛选
      await DataStore.selectNodeAddCleanNode()
      const { historyList } = await DataStore.getHistoryList()
      const currentHistory = historyList.slice(-1)[0]
      data.id = currentHistory.data.id
      data.table = currentHistory.data.table
    }
    await DataStore.addHistory({
      data,
    })
    DataStore.setConfirmEditTime(+new Date()) // 更新时间，触发后续节点的更新
    DataStore.resetDataCleanAction()

    this.$emit('close')
    this.resetCleanData()
  }

  private formatDescription(filter: any[]): string {
    let description = ''
    filter.forEach((data: any[]) => {
      data.forEach((d) => {
        description += `${d.col}:`
        if (d.filterType.includes('[a,b)')) {
          description += `${d.values.join('-')},`
        } else {
          const { type } = this.parsePieChartLegendAction()
          if (type === 'reverse') {
            const value = this.legendItems.filter((v) => !d.values.includes(v))
            description += `${value.join('、')}${value.length}个值`
          } else {
            description += `${d.values.join('、')}${d.values.length}个值`
          }
        }
      })
    })
    description =
      (this.cleanActionType === 'keep' ? '仅保留 ' : '删除 ') + description
    return `可视化清洗：${description}`
  }

  /**
   * 开始拖动 modal-title
   */
  private onDragStart(event: MouseEvent) {
    this.isDraging = true
    this.originMouseData = {
      x: event.screenX,
      y: event.screenY,
    }
    this.disableUserSelect()
    document.addEventListener('mousemove', this.onDragMove)
    document.addEventListener('mouseup', this.onDragStop)
  }

  private onDragMove(event: MouseEvent) {
    if (!this.isDraging) {
      return
    }
    const { x = 0, y = 0 } = this.originMouseData
    const { screenX, screenY } = event
    let { top, left } = this.dialogStyle
    top = +top.replace('px', '')
    left = +left.replace('px', '')
    this.dialogStyle = {
      top: `${top + screenY - y}px`,
      left: `${left + screenX - x}px`,
    }
    this.originMouseData = {
      x: screenX,
      y: screenY,
    }
  }

  private onDragStop() {
    this.isDraging = false
    let { top, left } = this.dialogStyle
    top = +top.replace('px', '')
    left = +left.replace('px', '')

    if (top < 0) {
      this.dialogStyle.top = '0px'
    }
    if (left < -(this.modalWidth - 100)) {
      this.dialogStyle.left = '0px'
    }
    this.originMouseData = {
      x: 0,
      y: 0,
    }
    this.enableUserSelect()
    document.removeEventListener('mousemove', this.onDragMove)
    document.removeEventListener('mouseup', this.onDragStop)
  }

  private onResizeStart(event: MouseEvent, mode: string) {
    this.isResizing = true
    this.resizingMode = mode
    this.resizeMouseData = {
      x: event.screenX,
      y: event.screenY,
    }
    this.documentHeight = document.documentElement.clientHeight
    this.disableUserSelect()
    document.addEventListener('mousemove', this.onResizeMove)
    document.addEventListener('mouseup', this.onResizeStop)
  }

  private onResizeMove(event: MouseEvent) {
    if (!this.isResizing) {
      return
    }
    const { x = 0, y = 0 } = this.resizeMouseData
    const { screenX, screenY } = event
    this.modalWidth += screenX - x
    this.modalHeight += screenY - y
    if (this.modalHeight > this.documentHeight) {
      this.modalHeight = this.documentHeight
    }
    this.resizeMouseData.x = screenX
    this.resizeMouseData.y = screenY
  }

  private onResizeStop() {
    this.isResizing = false
    document.removeEventListener('mousemove', this.onResizeMove)
    document.removeEventListener('mouseup', this.onResizeStop)
    this.originMouseData = {}

    const data = { ...this.currentGridItem.chartOptions }
    data.size = this.chartSize
    const [, chartType] = this.chart.chartType
    if (chartType !== ChartDetailsEnum.geographicMap) {
      // 直接改chart config的 size 会引起组件重绘
      ;(this.$refs.chart as any).changeSize(this.chartSize)
    } else {
      this.initialChartSize = this.chartSize // !!!地图组件临时方案
    }
    this.chartInstance.changeSize(...this.chartSize)
    this.enableUserSelect()
  }

  /**
   * 禁用用户选择，用于 drag、resize 时
   */
  private disableUserSelect() {
    document.documentElement.style.userSelect = 'none'
  }

  /**
   * 启用用户选择
   */
  private enableUserSelect() {
    document.documentElement.style.userSelect = 'auto'
  }
}
</script>

<style lang="less" scoped>
.modal-content {
  background-color: #fff;
  height: 480px;
  margin: -24px;
}

.content-text {
  color: #5d637e;
}

.graph-box {
  background-color: #fff;
  border: 1px solid;
  border-color: #e0e0e0;
  height: 100px;
  margin-top: 10px;
  width: 174px;
}

.graph-selected {
  border-color: #00f;
}

.window-title {
  display: inline-block;
  font-size: 120%;
  font-weight: 600;
  height: 32px;
}

.window-left {
  border: 1px solid #e0e0e0;
  border-bottom: 0;
  height: 480px;
}

.window-left-menubar {
  color: #5d637e;
  margin: 12px;
}

.window-left-graphs {
  border-top: 1px solid;
  border-top-color: #e0e0e0;
  height: 430px;
  width: 100%;
}

.window-left-buttons {
  background-color: #fff;
  border: 0;
  font-size: 25px;
  height: 45px;
  padding: 0 10px;
  width: 45px;

  .left-icon {
    width: 100%;
  }

  &:hover {
    background-color: fade(#f0f3ff, 50%);
    color: fade(#f9f9fc, 50%);
  }

  &:focus {
    background-color: fade(#5561ff, 15%);
    color: fade(#5561ff, 15%);
  }
}

.window-content {
  border-left: 1px solid;
  border-left-color: #e0e0e0;
  height: 430px;
  padding: 5px;
}

.window-content-graph {
  background-color: #f0f3ff;
  height: 26px;
}

.window-content-imgs {
  font-size: 20px;
  height: 70px;
  margin-left: 5px;
  margin-top: -5px;
  width: 160px;
}

.window-middle {
  background-color: #f6f6f6;
  border-top: 1px solid #e0e0e0;
  height: 480px;
  position: relative;

  .clean-action {
    height: 36px;
    line-height: 36px;
    overflow: hidden;
    padding: 0 30px;
    position: absolute;
    right: 0;
    width: 100%;
    z-index: 310;

    .brush-data-info {
      margin-right: 16px;

      label {
        color: #999;
        margin-right: 12px;
      }
    }
  }
}

.window-middle-canvas {
  background-color: #fff;
  height: 420px;
  margin: 36px 30px 30px;
  overflow: hidden;
  position: relative;

  .empty-info-box {
    height: 100%;
    text-align: center;

    img {
      margin-top: 50px;
      width: 80%;
    }
  }

  .empty-info {
    color: #999;
    position: relative;
  }
}

.window-right {
  border: 1px solid;
  border-color: #e0e0e0;
  height: 480px;
  overflow-y: scroll;
}

.modal-title {
  padding: 16px 24px;
}

.resize-nwse {
  bottom: -1px;
  cursor: nwse-resize;
  height: 16px;
  position: absolute;
  right: -1px;
  width: 4px;
}

.brush-clean-action {
  align-items: center;
  display: flex;

  .wrapper {
    flex: 1;
    max-width: 70%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .btn-more {
    cursor: pointer;
    font-size: 12px;
    margin: 0 8px 0 5px;
  }
}
</style>
<style lang="less">
.visualization-preview-modal {
  .ant-modal-header {
    padding: 0;
  }

  .ant-modal {
    margin: 0;
  }
}
</style>
